package com.yyfax.pay.depository.service.inner.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Function;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.yyfax.framework.common.Log;
import com.yyfax.framework.common.OpResponse;
import com.yyfax.framework.service.CacheService;
import com.yyfax.pay.common.constants.CacheName;
import com.yyfax.pay.common.constants.PayConstants;
import com.yyfax.pay.common.enums.FileServcerStateEnum;
import com.yyfax.pay.common.enums.FuiouBusiTypeEnum;
import com.yyfax.pay.common.enums.FuiouDepositRspCodeEnum;
import com.yyfax.pay.common.enums.FuiouDepositStateEnum;
import com.yyfax.pay.common.enums.PayTypeEnum;
import com.yyfax.pay.common.exception.PayExceptionCode;
import com.yyfax.pay.common.util.FuiouIdUtils;
import com.yyfax.pay.depository.dao.FuiouFileDownloadDao;
import com.yyfax.pay.depository.dao.FuiouFileDownloadDetailDao;
import com.yyfax.pay.depository.dao.FuiouFileUploadDao;
import com.yyfax.pay.depository.dao.FuiouFileUploadDetailDao;
import com.yyfax.pay.depository.domain.FuiouFileDownload;
import com.yyfax.pay.depository.domain.FuiouFileDownloadDetail;
import com.yyfax.pay.depository.domain.FuiouFileUpload;
import com.yyfax.pay.depository.domain.FuiouFileUploadDetail;
import com.yyfax.pay.depository.service.inner.FuiouDepositCommonService;
import com.yyfax.pay.depository.service.inner.FuiouDepositFileInnerService;
import com.yyfax.pay.depository.vo.FuiouDownloadFileVO;
import com.yyfax.pay.depository.vo.FuiouFileQueryVO;
import com.yyfax.pay.depository.vo.FuiouUploadFileVO;
import com.yyfax.pay.order.service.PayCommonService;
import com.yyfax.pay.third.facade.file.FileServerService;
import com.yyfax.pay.third.model.to.file.DownloadNotifyReq;
import com.yyfax.pay.third.model.to.file.FileResultDetailVO;
import com.yyfax.pay.third.model.to.file.QueryNotifyReq;
import com.yyfax.pay.third.model.to.file.UploadNotifyReq;
import com.yyfax.pay.third.model.to.fuioudeposit.UploadFileResultPushReq;
import com.yyfax.pay.third.model.to.fuioudeposit.UploadFileResultPushReq.UploadFileResultBody;
import com.yyfax.pay.vo.depository.DownloadFileReq;
import com.yyfax.pay.vo.depository.FileQueryNotifyRsp;
import com.yyfax.pay.vo.depository.FileQueryReq;
import com.yyfax.pay.vo.depository.FileResultRsp;
import com.yyfax.pay.vo.depository.FuiouFileDetailResultRsp;
import com.yyfax.pay.vo.depository.UploadFilePushResultRsp;
import com.yyfax.pay.vo.depository.UploadFilePushResultRsp.UploadFilePushResultDetail;
import com.yyfax.pay.vo.depository.UploadFileReq;

@Service
public class FuiouDepositFileInnerServiceImpl implements FuiouDepositFileInnerService {
	private Logger logger = LoggerFactory.getLogger(getClass());

	@Resource
	private FuiouIdUtils fuiouIdUtils;

	@Resource
	private FuiouFileUploadDao fuiouFileUploadDao;

	@Resource
	private FuiouFileUploadDetailDao fuiouFileUploadDetailDao;

	@Resource
	private FuiouFileDownloadDao fuiouFileDownloadDao;

	@Resource
	private FuiouFileDownloadDetailDao fuiouFileDownloadDetailDao;

	@Resource
	private FileServerService fileServerService;

	@Resource
	private FuiouDepositCommonService fuiouDepositCommonService;

	@Resource
	private PayCommonService payCommonService;

	@Resource(name = "payCacheService")
	private CacheService cacheService;

	@Override
	@Transactional
	public FuiouUploadFileVO saveUpload(UploadFileReq params, FuiouBusiTypeEnum type) {
		final String op = "FuiouDepositFileInnerServiceImpl.saveUpload";
		String merchantCode = params.getMerchantCode();

		FuiouFileUpload po = new FuiouFileUpload();
		List<FuiouFileUploadDetail> list = new ArrayList<>();

		String busiId = fuiouIdUtils.generateBusiId(FuiouBusiTypeEnum.FILE_TRADE_UPLOAD, merchantCode);
		po.setBusiId(busiId);
		po.setType(type.getValue());
		po.setOrderId(params.getOrderId());
		po.setOrderTime(params.getOrderTime());
		po.setMerchantCode(merchantCode);
		po.setCount(params.getFileNames().size());
		po.setOrigDir(params.getOrigDir());
		po.setDestDir(params.getDestDir());
		po.setNotifyUrl(params.getNotifyUrl());
		po.setPushUrl(params.getPushUrl());
		po.setState(FuiouDepositStateEnum.INI.getValue());

		try {
			fuiouFileUploadDao.insert(po);
		} catch (DuplicateKeyException e) {
			logger.warn(Log.op(op).msg("保存文件上传订单信息主键冲突").kv("merchantCode", merchantCode).kv("orderId", params.getOrderId()).toString());
			po = fuiouFileUploadDao.selectByMerchantCodeAndOrderId(merchantCode, params.getOrderId());
			if (po == null) {
				logger.error(
						Log.op(op).msg("保存文件上传订单信息主键冲突,查询订单的不存在").kv("merchantCode", merchantCode).kv("orderId", params.getOrderId()).toString());
				throw PayExceptionCode.ORDER_NOT_FOUND.exp();
			}

			list = fuiouFileUploadDetailDao.selectListByBusiId(merchantCode, po.getBusiId());

			return FuiouUploadFileVO.newInstance(po, list);
		}

		for (String fileName : params.getFileNames()) {
			FuiouFileUploadDetail detail = new FuiouFileUploadDetail();
			detail.setMerchantCode(merchantCode);
			detail.setFileName(fileName);
			detail.setBusiId(busiId);
			detail.setState(FuiouDepositStateEnum.INI.getValue());

			list.add(detail);
		}

		try {
			fuiouFileUploadDetailDao.insertBatch(list);
		} catch (DuplicateKeyException e) {
			logger.error(Log.op(op).msg("保存文件上传详细订单文件名不存在").kv("merchantCode", merchantCode).kv("orderId", params.getOrderId()).toString(), e);
			throw PayExceptionCode.ERROR_ILL_PARAMETER.expMsg("文件重复");
		}

		return FuiouUploadFileVO.newInstance(po, list);
	}

	@Override
	public FuiouFileUpload doUpload(FuiouUploadFileVO vo) {
		final String op = "FuiouDepositFileInnerServiceImpl.doUpload";
		FuiouFileUpload po = vo.getPo();

		if (!vo.isDo()) {
			// 判断文件不能上传,则直接返回
			// 如果入参vo和出参FuiouFileUpload存在编辑的情况，则该方法不适用，需要做深度copy，不然会出现脏读
			return po;
		}

		// 更新订单状态初始化到"上传中"
		int i = fuiouFileUploadDao.updateState(po.getBusiId(), FuiouDepositStateEnum.INI.getValue(), FuiouDepositStateEnum.DEALING.getValue(),
				PayExceptionCode.PROCESSING.getCode(), PayExceptionCode.PROCESSING.getDesc());

		if (i <= 0) {
			// 如果更新没有命中，则返回
			logger.warn(Log.op(op).msg("上传文件，更新状态没有命中").kv("origState", FuiouDepositStateEnum.INI.getValue())
					.kv("destState", FuiouDepositStateEnum.DEALING.getValue()).kv("busiId", po.getBusiId()).toString());

			return fuiouFileUploadDao.selectByPrimaryKey(po.getBusiId());
		}

		// 发送文件到文件服务器
		OpResponse response = fileServerService.doUpload(vo);
		int rspCode = response != null ? response.getCode() : PayExceptionCode.ERROR_FILE_FAIL.getCode();
		String rspMsg = response != null ? response.getDisplay() : PayExceptionCode.ERROR_FILE_FAIL.getDesc();

		FuiouDepositStateEnum state = FuiouDepositStateEnum.FAIL;
		if (response != null) {
			if (PayExceptionCode.SUCCESS.isCode(response.getCode())) {
				state = FuiouDepositStateEnum.SUCCESS;
			} else if (PayExceptionCode.WARN_REQ_TIME_OUT.isCode(response.getCode())) {
				// 如果是超时，则更新状态为超时
				state = FuiouDepositStateEnum.TIMEOUT;
			}
		}

		po.setState(state.getValue());
		po.setRspCode(rspCode);
		po.setRspMsg(rspMsg);

		// 根据响应结果，更新文件订单状态
		int j = fuiouFileUploadDao.updateState(po.getBusiId(), FuiouDepositStateEnum.DEALING.getValue(), state.getValue(), rspCode, rspMsg);
		if (j <= 0) {
			logger.warn(Log.op(op).msg("上传文件，更新状态没有命中").kv("origState", FuiouDepositStateEnum.DEALING.getValue())
					.kv("destState", state.getValue().toString()).kv("busiId", po.getBusiId()).toString());
		}

		// 如果入参vo和出参FuiouFileUpload存在编辑的情况，则该方法不适用，需要做深度copy，不然会出现脏读
		return po;
	}

	@Override
	@Transactional
	public FuiouDownloadFileVO saveDownload(DownloadFileReq params, FuiouBusiTypeEnum type) {
		final String op = "FuiouDepositFileInnerServiceImpl.saveDownload";

		String merchantCode = params.getMerchantCode();

		FuiouFileDownload po = new FuiouFileDownload();
		List<FuiouFileDownloadDetail> list = new ArrayList<>();

		String busiId = fuiouIdUtils.generateBusiId(FuiouBusiTypeEnum.FILE_TRADE_DOWNLOAD, merchantCode);
		po.setBusiId(busiId);
		po.setType(type.getValue());
		po.setOrderId(params.getOrderId());
		po.setOrderTime(params.getOrderTime());
		po.setMerchantCode(merchantCode);
		po.setCount(params.getFileNames().size());
		po.setOrigDir(params.getOrigDir());
		po.setDestDir(params.getDestDir());
		po.setNotifyUrl(params.getNotifyUrl());
		po.setState(FuiouDepositStateEnum.INI.getValue());

		try {
			fuiouFileDownloadDao.insert(po);
		} catch (DuplicateKeyException e) {
			logger.warn(Log.op(op).msg("保存文件下载订单信息主键冲突").kv("merchantCode", merchantCode).kv("orderId", params.getOrderId()).toString());
			po = fuiouFileDownloadDao.selectByMerchantCodeAndOrderId(merchantCode, params.getOrderId());
			if (po == null) {
				logger.error(
						Log.op(op).msg("保存文件下载订单信息主键冲突,查询订单的不存在").kv("merchantCode", merchantCode).kv("orderId", params.getOrderId()).toString());
				throw PayExceptionCode.ORDER_NOT_FOUND.exp();
			}

			list = fuiouFileDownloadDetailDao.selectListByBusiId(merchantCode, po.getBusiId());

			return FuiouDownloadFileVO.newInstance(po, list);
		}

		for (String fileName : params.getFileNames()) {
			FuiouFileDownloadDetail detail = new FuiouFileDownloadDetail();
			detail.setMerchantCode(merchantCode);
			detail.setFileName(fileName);
			detail.setBusiId(busiId);
			detail.setState(FuiouDepositStateEnum.INI.getValue());

			list.add(detail);
		}

		try {
			fuiouFileDownloadDetailDao.insertBatch(list);
		} catch (DuplicateKeyException e) {
			logger.error(Log.op(op).msg("保存文件下载详细订单文件名重复").kv("merchantCode", merchantCode).kv("orderId", params.getOrderId()).toString(), e);
			throw PayExceptionCode.ERROR_ILL_PARAMETER.expMsg("文件重复");
		}

		return FuiouDownloadFileVO.newInstance(po, list);
	}

	@Override
	public FuiouFileDownload doDownload(FuiouDownloadFileVO vo) {
		final String op = "FuiouDepositFileInnerServiceImpl.doDownload";

		FuiouFileDownload po = vo.getPo();

		if (!vo.isDo()) {
			// 下载文件已经处理，则直接返回
			// 如果入参vo和出参FuiouFileUpload存在编辑的情况，则该方法不适用，需要做深度copy，不然会出现脏读
			return po;
		}

		// 更新订单状态初始化到“处理中”
		int i = fuiouFileDownloadDao.updateStateSelective(po.getBusiId(), FuiouDepositStateEnum.INI.getValue(),
				FuiouDepositStateEnum.DEALING.getValue(), PayExceptionCode.PROCESSING.getCode(), PayExceptionCode.PROCESSING.getDesc());
		if (i <= 0) {
			// 如果更新没有命中，则返回
			logger.warn(Log.op(op).msg("下载文件，更新状态没有命中").kv("origState", FuiouDepositStateEnum.INI.getValue())
					.kv("destState", FuiouDepositStateEnum.DEALING.getValue()).toString());

			return fuiouFileDownloadDao.selectByPrimaryKey(po.getBusiId());
		}

		// 发送请求到文件服务器
		OpResponse response = fileServerService.doDownload(vo);
		int rspCode = response != null ? response.getCode() : PayExceptionCode.ERROR_FILE_FAIL.getCode();
		String rspMsg = response != null ? response.getDisplay() : PayExceptionCode.ERROR_FILE_FAIL.getDesc();

		FuiouDepositStateEnum state = FuiouDepositStateEnum.FAIL;
		if (response != null) {
			if (PayExceptionCode.SUCCESS.isCode(response.getCode())) {
				state = FuiouDepositStateEnum.SUCCESS;
			} else if (PayExceptionCode.WARN_REQ_TIME_OUT.isCode(response.getCode())) {
				// 如果是超时，则更新状态为超时
				state = FuiouDepositStateEnum.TIMEOUT;
			}
		}

		po.setState(state.getValue());
		po.setRspCode(rspCode);
		po.setRspMsg(rspMsg);

		// 根据响应结果，更新文件订单状态
		int j = fuiouFileDownloadDao.updateStateSelective(vo.getPo().getBusiId(), FuiouDepositStateEnum.DEALING.getValue(), state.getValue(), rspCode,
				rspMsg);

		if (j <= 0) {
			logger.warn(Log.op(op).msg("下载文件，更新状态没有命中").kv("origState", FuiouDepositStateEnum.DEALING.getValue())
					.kv("destState", state.getValue().toString()).kv("busiId", po.getBusiId()).toString());
		}

		// 如果入参vo和出参FuiouFileUpload存在编辑的情况，则该方法不适用，需要做深度copy，不然会出现脏读
		return po;
	}

	@Override
	public void parseDownloadResultForFileServer(String merchantCode, DownloadNotifyReq req) {
		List<FuiouFileDownloadDetail> list = Lists.newArrayList();
		try {
			FuiouFileDownload po = fuiouFileDownloadDao.selectByPrimaryKey(req.getOrderId());
			if (po == null) {
				logger.error(Log.op("FuiouDepositFileInnerServiceImpl.parseDownloadResultForFileServer").msg("解析下载结果，订单不存在")
						.kv("busiId", req.getOrderId()).toString());
				return;
			}

			for (FileResultDetailVO file : req.getFiles()) {
				FuiouFileDownloadDetail detail = new FuiouFileDownloadDetail();
				detail.setMerchantCode(po.getMerchantCode());
				detail.setBusiId(po.getBusiId());
				detail.setFileName(file.getFileName());
				String state = FuiouDepositStateEnum.parse(FileServcerStateEnum.fromValue(file.getState())).getValue();
				detail.setState(state);

				list.add(detail);
			}

			// 增加按照文件名排序，避免批量更新出现死锁
			Collections.sort(list, new Comparator<FuiouFileDownloadDetail>() {
				@Override
				public int compare(FuiouFileDownloadDetail o1, FuiouFileDownloadDetail o2) {
					return ComparisonChain.start().compare(o1.getFileName(), o2.getFileName(), Ordering.natural()).result();
				}

			});

			fuiouFileDownloadDetailDao.updateBatch(list);

			// 生成异步通知
			generateDownResultNotifyTask(FuiouDownloadFileVO.newInstance(po, list));
		} finally {
			list = null;
		}
	}

	/**
	 * 生成下载结果异步通知
	 * 
	 * @author buyi
	 * @date 2017-09-20 10:28:09
	 * @since v2.0.0
	 * @param vo
	 */
	private void generateDownResultNotifyTask(FuiouDownloadFileVO vo) {
		try {

			FuiouFileDownload po = vo.getPo();

			List<FuiouFileDetailResultRsp> list = Lists.transform(vo.getList(), new Function<FuiouFileDownloadDetail, FuiouFileDetailResultRsp>() {

				@Override
				public FuiouFileDetailResultRsp apply(FuiouFileDownloadDetail input) {
					FuiouFileDetailResultRsp rsp = new FuiouFileDetailResultRsp();

					rsp.setFileName(input.getFileName());
					rsp.setState(input.getState());
					return rsp;
				}
			});

			FileResultRsp rsp = new FileResultRsp();
			rsp.setOrderId(vo.getPo().getOrderId());
			rsp.setFiles(list);

			// 封装请求头部
			Map<String, String> reqHeader = Maps.newHashMap();
			reqHeader.put(PayConstants.FIELD_MERCHANT_CODE, vo.getPo().getMerchantCode());
			reqHeader.put(PayConstants.FIELD_SIGN, payCommonService.getSignJson(vo.getPo().getMerchantCode(), rsp));

			fuiouDepositCommonService.saveNotifyTask(po.getMerchantCode(), po.getOrderId(), po.getBusiId(), FuiouBusiTypeEnum.FILE_TRADE_DOWNLOAD,
					po.getNotifyUrl(), reqHeader, JSONObject.toJSONString(rsp), null, null);

		} catch (Exception e) {
			logger.error(Log.op("FuiouDepositFileInnerServiceImpl.generateDownResultNotifyTask").msg("生成下载结果异步通知异常")
					.kv("busiId", vo.getPo().getBusiId()).toString(), e);
		}

	}

	@Override
	public String parseUploadPushResult(String merchantCode, UploadFileResultPushReq req, PayTypeEnum payTypeEnum) {
		final String op = "FuiouDepositFileInnerServiceImpl.parseUploadPushResult";
		if (req == null || req.getBody() == null) {
			logger.error(Log.op(op).msg("解析回盘推送接口，请求参数UploadFileResultPushReq为空").toString());
			return null;
		}

		/*
		 * 根据商户号，文件名查询，所以商户方保证文件名称的唯一性，才能保证数据唯一性
		 */
		Map<String, FuiouUploadFileVO> map = Maps.newHashMap();
		// 上传文件详细订单集合，用于更新订单信息
		List<FuiouFileUploadDetail> updateList = Lists.newArrayList();
		try {

			String uploadFileName = "";
			// step1.更新上传文件处理结果
			for (UploadFileResultBody file : req.getBody()) {

				// 操蛋的报备的业务，上传处理结果异步推送的文件名为：日期_fileName.txt,如果是报备类型，则截取文件名才能获得原上传文件名
				// uploadFileName = PayTypeEnum.FILE_TRADE.equals(payTypeEnum) ? file.getFileName() : file.getFileName().substring(9);
				uploadFileName = PayTypeEnum.FILE_TRADE.isValue(payTypeEnum.getValue()) ? file.getFileName() : file.getFileName().substring(9);

				List<FuiouFileUploadDetail> details = fuiouFileUploadDetailDao.selectByFileName(merchantCode, uploadFileName);
				if (CollectionUtils.isEmpty(details) || details.size() != 1) {
					logger.error(Log.op("FuiouDepositFileInnerServiceImpl.parseUploadPushResult").msg("富友存管文件上传结果推送，文件名重复，或者不存在")
							.kv("merchantCode", merchantCode).kv("fileName", uploadFileName).kv("listSize", details.size()).toString());
					continue;
				}

				FuiouFileUploadDetail detail = details.get(0);

				detail.setCount(file.getSumNum());
				detail.setSuccessCount(file.getSuccNum());
				detail.setFailCount(file.getFailNum());
				detail.setState(FuiouDepositStateEnum.SUCCESS.getValue());

				if (PayTypeEnum.FILE_CHECK.isValue(payTypeEnum.getValue())) {
					// 如果是报备，则备忘保存回盘文件名
					detail.setRemark(file.getFileName());
				}

				if (map.containsKey(detail.getBusiId())) {
					map.get(detail.getBusiId()).getList().add(detail);
				} else {
					FuiouFileUpload po = fuiouFileUploadDao.selectByPrimaryKey(detail.getBusiId());
					List<FuiouFileUploadDetail> list = Lists.newArrayList();
					list.add(detail);

					map.put(po.getBusiId(), FuiouUploadFileVO.newInstance(po, list));
				}

				// 富友异步回调处理结果，是乱序你，执行单数据更新
				updateList.add(detail);
			}

			logger.info(Log.op(op).msg("推送文件数和实际处理文件数").kv("pushSize", req.getBody().size()).kv("actSize", updateList.size()).toString());

			// 批量更新订单
			if (!CollectionUtils.isEmpty(updateList)) {

				// 增加按照文件名排序，避免批量更新出现死锁
				Collections.sort(updateList, new Comparator<FuiouFileUploadDetail>() {
					@Override
					public int compare(FuiouFileUploadDetail o1, FuiouFileUploadDetail o2) {
						return ComparisonChain.start().compare(o1.getFileName(), o2.getFileName(), Ordering.natural()).result();
					}

				});

				fuiouFileUploadDetailDao.updateResultBatch(updateList);
			}

			// step2.生成通知
			generateUploadPushResultNotifyTask(map, payTypeEnum);

			// step3.封装响应参数
			JSONObject json = new JSONObject();
			json.put("rspCode", FuiouDepositRspCodeEnum.SUCCESS.getValue());
			json.put("rspDesc", "成功");
			return json.toJSONString();
		} finally {
			map = null;
		}
	}

	/**
	 * 生成上传文件处理推送结果的异步通知惹惹我怒
	 * 
	 * @author buyi
	 * @date 2017-09-08 10:59:48
	 * @since v2.0.0
	 * @param map key-业务主键，value-上传文件快照类
	 */
	private void generateUploadPushResultNotifyTask(Map<String, FuiouUploadFileVO> map, PayTypeEnum payTypeEnum) {
		try {
			if (map == null || map.isEmpty()) {
				return;
			}

			for (String busiId : map.keySet()) {
				FuiouUploadFileVO vo = map.get(busiId);

				UploadFilePushResultRsp params = new UploadFilePushResultRsp();
				params.setOrderId(vo.getPo().getOrderId());

				List<UploadFilePushResultDetail> files = Lists.newArrayList();

				for (FuiouFileUploadDetail detail : vo.getList()) {
					UploadFilePushResultDetail resultDetail = new UploadFilePushResultDetail();
					resultDetail.setFileName(detail.getFileName());
					resultDetail.setCount(detail.getCount());
					resultDetail.setFailCount(detail.getFailCount());
					resultDetail.setSuccessCount(detail.getSuccessCount());

					if (PayTypeEnum.FILE_CHECK.isValue(payTypeEnum.getValue())) {
						// 如果是报备，则备忘保存回盘文件名
						resultDetail.setOverFileName(detail.getRemark());
					}

					files.add(resultDetail);

				}

				params.setFiles(files);

				// 封装请求头部
				Map<String, String> reqHeader = Maps.newHashMap();
				reqHeader.put(PayConstants.FIELD_MERCHANT_CODE, vo.getPo().getMerchantCode());
				reqHeader.put(PayConstants.FIELD_SIGN, payCommonService.getSignJson(vo.getPo().getMerchantCode(), params));

				// 任务业务主键，推送存在同一个批次号，多次推送的场景，需要拼装时间戳
				String taskBusiId = vo.getPo().getBusiId().concat("_").concat(System.currentTimeMillis() + "");

				fuiouDepositCommonService.saveNotifyTask(vo.getPo().getMerchantCode(), vo.getPo().getOrderId(), taskBusiId,
						FuiouBusiTypeEnum.FILE_TRADE_PUSH, vo.getPo().getPushUrl(), reqHeader, JSONObject.toJSONString(params), null, null);
			}
		} catch (Exception e) {
			logger.error(Log.op("FuiouDepositFileInnerServiceImpl.generateUploadPushResultNotifyTask").msg("生成上传文件处理结果异步通知任务异常")
					.kv("busiIds", map.keySet()).toString(), e);
		}
	}

	@Override
	public void validFileServerSign(String merchantCode, String sign, String body, PayTypeEnum payTypeEnum) {
		boolean isSign = fileServerService.validFileSign(merchantCode, sign, body, payTypeEnum);
		if (!isSign) {
			throw PayExceptionCode.ERROR_SIGN_FAIL.exp();
		}
	}

	@Override
	public void parseUploadResultForFileServer(String merchantCode, UploadNotifyReq req) {
		final String op = "FuiouDepositFileInnerServiceImpl.parseUploadResultForFileServer";

		List<FuiouFileUploadDetail> list = Lists.newArrayList();
		try {
			FuiouFileUpload po = fuiouFileUploadDao.selectByPrimaryKey(req.getOrderId());

			if (po == null) {
				logger.error(Log.op(op).msg("没有找到文件上传订单").kv("busiId", req.getOrderId()).toString());
				return;
			}

			for (FileResultDetailVO file : req.getFiles()) {
				FuiouFileUploadDetail detail = new FuiouFileUploadDetail();
				detail.setMerchantCode(po.getMerchantCode());
				detail.setBusiId(po.getBusiId());
				detail.setFileName(file.getFileName());
				String state = FuiouDepositStateEnum.parse(FileServcerStateEnum.fromValue(file.getState())).getValue();
				detail.setState(state);
				// fuiouFileUploadDetailDao.updateResult(detail);

				list.add(detail);
			}

			// 增加按照文件名排序，避免批量更新出现死锁
			Collections.sort(list, new Comparator<FuiouFileUploadDetail>() {
				@Override
				public int compare(FuiouFileUploadDetail o1, FuiouFileUploadDetail o2) {
					return ComparisonChain.start().compare(o1.getFileName(), o2.getFileName(), Ordering.natural()).result();
				}
			});

			// 批量给更新详细订单信息
			fuiouFileUploadDetailDao.updateResultBatch(list);

			// 生成异步通知
			generateUploadResultNotifyTask(FuiouUploadFileVO.newInstance(po, list));
		} finally {
			list = null;
		}
	}

	/**
	 * 生成上传结果通知任务
	 * 
	 * @author buyi
	 * @date 2017-09-20 11:01:06
	 * @since v2.0.0
	 * @param vo
	 */
	private void generateUploadResultNotifyTask(FuiouUploadFileVO vo) {
		final String op = "FuiouDepositFileInnerServiceImpl.generateUploadResultNotifyTask";

		if (vo == null) {
			logger.warn(Log.op(op).msg("参数FuiouUploadFileVO为空，不生上传结果异步通知任务").toString());
			return;
		}

		// 异步通知结果报文实体
		FileResultRsp rsp = new FileResultRsp();
		// 异步通知结果集合实体
		List<FuiouFileDetailResultRsp> list = null;

		try {
			FuiouFileUpload po = vo.getPo();

			// 将文件详细信息转换成结果通知报文实体
			list = Lists.transform(vo.getList(), new Function<FuiouFileUploadDetail, FuiouFileDetailResultRsp>() {
				@Override
				public FuiouFileDetailResultRsp apply(FuiouFileUploadDetail input) {
					FuiouFileDetailResultRsp rsp = new FuiouFileDetailResultRsp();

					rsp.setFileName(input.getFileName());
					rsp.setState(input.getState());
					return rsp;
				}
			});

			rsp.setOrderId(vo.getPo().getOrderId());
			rsp.setFiles(list);

			// 封装请求头部
			Map<String, String> reqHeader = Maps.newHashMap();
			reqHeader.put(PayConstants.FIELD_MERCHANT_CODE, vo.getPo().getMerchantCode());
			reqHeader.put(PayConstants.FIELD_SIGN, payCommonService.getSignJson(vo.getPo().getMerchantCode(), rsp));

			fuiouDepositCommonService.saveNotifyTask(po.getMerchantCode(), po.getOrderId(), po.getBusiId(), FuiouBusiTypeEnum.FILE_TRADE_UPLOAD,
					po.getNotifyUrl(), reqHeader, JSONObject.toJSONString(rsp), null, null);
		} catch (Exception e) {
			logger.error(Log.op(op).msg("生成上传文件结果异步通知任务异常").kv("busiId", vo.getPo().getBusiId()).toString(), e);
		} finally {
			// 批量文件，存在大实例，直接回收
			list = null;
			rsp = null;
		}

	}

	@Override
	public FuiouFileQueryVO saveQueryOrder(FileQueryReq params, PayTypeEnum type) {
		final String op = "FuiouDepositFileInnerServiceImpl.saveQueryOrder";
		String busiId = params.getMerchantCode().concat(params.getOrderId()).concat("-").concat(type.getName());
		String key = getQueryCacheKey(busiId);

		FuiouFileQueryVO vo = new FuiouFileQueryVO();
		vo.setBusiId(busiId);
		vo.setMerchantCode(params.getMerchantCode());
		vo.setOrderId(params.getOrderId());
		vo.setDestDir(params.getDestDir());
		vo.setFiles(params.getFileNames());
		vo.setNotifyUrl(params.getNotifyUrl());
		vo.setType(type);

		boolean result = cacheService.add(key, vo);
		if (!result) {
			logger.warn(Log.op(op).msg("保存查询回盘文件结果到缓存失败，可能存在缓存关键字").kv("merchantCode", params.getMerchantCode()).kv("orderId", params.getOrderId())
					.kv("cacheKey", key).toString());
			// 如果保存到memcache失败
			vo = cacheService.get(key);
			if (vo == null) {
				logger.error(Log.op(op).msg("保存查询回盘文件结果到缓存，关键字冲突，查询订单的不存在").kv("merchantCode", params.getMerchantCode())
						.kv("orderId", params.getOrderId()).kv("cacheKey", key).toString());
				throw PayExceptionCode.ORDER_NOT_FOUND.exp();
			}

		}

		return vo;
	}

	/**
	 * 获取查询缓存key
	 * 
	 * @author buyi
	 * @date 2017-10-09 16:47:46
	 * @since v2.0.0
	 * @param merchantCode
	 * @param orderId
	 * @param type
	 * @return
	 */
	private String getQueryCacheKey(String busiId) {
		return CacheName.FUIOU_DEPOSIT_FILE_QUERY.concat("-").concat(busiId);
	}

	@Override
	public OpResponse query(FuiouFileQueryVO vo) {
		// 调用文件服务器的查询接口
		return fileServerService.query(vo);
	}

	@Override
	public void parseQueryNotify(String merchantCode, QueryNotifyReq req, PayTypeEnum type) {
		final String op = "FuiouDepositFileInnerServiceImpl.parseQueryNotify";
		String key = getQueryCacheKey(req.getOrderId());
		FuiouFileQueryVO vo = null;
		try {
			vo = cacheService.get(key);
			if (vo == null) {
				logger.error(Log.op(op).msg("没有找到回盘文件查询订单").kv("merchantCode", merchantCode).kv("busiId", req.getOrderId()).kv("cacheKey", key)
						.toString());
				return;
			}

			if (CollectionUtils.isEmpty(req.getFiles())) {
				logger.warn(
						Log.op(op).msg("文件查询 | 查询结果异步通知文件集合为空，不处理任何业务").kv("merchantCode", merchantCode).kv("busiId", req.getOrderId()).toString());
				return;
			}

			// 解析结果,文件系统的返回结果和支付定义的接口一模一样,且以文件服务回调的结果为主，直接用
			vo.setFilesResult(req.getFiles());

			// 生成异步通知任务
			generateQueryResultNotifyTask(vo);
		} finally {
			vo = null;

			// 解析后，任务持久化到数据库，订单信息从缓存中删除
			cacheService.delete(key);
		}
	}

	/**
	 * 生成回盘文件查询结果异步通知
	 * 
	 * @author buyi
	 * @date 2017-10-10 08:59:46
	 * @since v2.0.0
	 * @param vo
	 */
	private void generateQueryResultNotifyTask(FuiouFileQueryVO vo) {
		final String op = "FuiouDepositFileInnerServiceImpl.generateQueryResultNotifyTask";
		if (vo == null) {
			logger.warn(Log.op(op).msg("参数FuiouUploadFileVO为空，不生上传结果异步通知任务").toString());
			return;
		}

		// 异步通知结果报文实体
		FileQueryNotifyRsp rsp = new FileQueryNotifyRsp();
		try {

			rsp.setOrderId(vo.getOrderId());
			rsp.setFiles(vo.getFilesResult());

			// 封装请求头部
			Map<String, String> reqHeader = Maps.newHashMap();
			reqHeader.put(PayConstants.FIELD_MERCHANT_CODE, vo.getMerchantCode());
			reqHeader.put(PayConstants.FIELD_SIGN, payCommonService.getSignJson(vo.getMerchantCode(), rsp));

			// FuiouBusiTypeEnum busiType = PayTypeEnum.FILE_TRADE.equals(vo.getType()) ? FuiouBusiTypeEnum.FILE_TRADE_QUERY
			// : FuiouBusiTypeEnum.FILE_CHECK_QUERY;
			FuiouBusiTypeEnum busiType = PayTypeEnum.FILE_TRADE.isValue(vo.getType().getValue()) ? FuiouBusiTypeEnum.FILE_TRADE_QUERY
					: FuiouBusiTypeEnum.FILE_CHECK_QUERY;

			fuiouDepositCommonService.saveNotifyTask(vo.getMerchantCode(), vo.getOrderId(), vo.getBusiId(), busiType, vo.getNotifyUrl(), reqHeader,
					JSONObject.toJSONString(rsp), null, null);
		} catch (Exception e) {
			logger.error(Log.op(op).msg("生成回盘文件查询结果异步通知任务异常").kv("merchantCode", vo.getMerchantCode()).kv("orderId", vo.getOrderId()).toString(), e);
		} finally {
			rsp = null;
		}
	}
}
